﻿using System;
using System.Collections;
using System.Linq;
using ATMCableCloud.CableCloudExceptions;

namespace ATMCableCloud
{
	namespace Graph
	{
		class NetworkGraph
		{
			NetworkGraph()
			{
				nodes_ = new ArrayList();
			}
			static NetworkGraph rInstance_;
			readonly ArrayList nodes_;
			/**
			 * singleton
			 * */
			public static NetworkGraph getInstance()
			{
				return rInstance_ ?? (rInstance_ = new NetworkGraph());
			}

			public Node find(string name)
			{
				try
				{
					foreach (Node node in nodes_.Cast<Node>().Where(node => node.name.Equals(name)))
						return node;
					throw new NodeNameNotFound(name);
				}
				catch (NodeNotFound e)
				{
					Console.WriteLine(e);
					throw;
				}
			}
			public void addNode(string name,
								int port)
			{
				try
				{
					var node = new Node(name);
					var i = (nodes_.Contains(node) ? nodes_.IndexOf(node) : nodes_.Add(node));
					(nodes_[i] as Node ).addPort(port) ;
				}
				catch(MultipleUsingEndpoint e)
				{
					Console.WriteLine(e);
					throw;
				}
			}
			public void addLink(string peer1Name,
								int peer1Port,
								string peer2Name,
								int peer2Port)
			{
				var node1 = nodes_.IndexOf(new Node(peer1Name));
				var node2 = nodes_.IndexOf(new Node(peer2Name));
				(nodes_[node1] as Node).addLink(nodes_[node2] as Node,
												peer1Port,
												peer2Port);

			}
			
			internal class Node
			{
				internal class Link
				{
					public Node remoteEndpoint { get; private set; }
					public Node localEndpoint { get; private set; }
					public int remotePort { get; private set; }
					public int localPort { get; private set; }
					public Link(Node local, int local_port,Node remote, int remote_port)
					{
						localEndpoint = local;
						remoteEndpoint = remote;
						remotePort = remote_port;
						localPort = local_port;
					}
				};
				public override bool Equals(object obj)
				{
					var node = (Node)obj;
					return node.name.Equals(name);
				}
				public override int GetHashCode()
				{
					return base.GetHashCode();
				}
				public override string ToString()
				{
					return name;
				}

				public void addPort(int port)
				{
					if(ports_.Contains(port))
						throw new MultipleUsingEndpoint(this, port);
					ports_.Add(port);
				}

				public Node(string name)
				{
					this.name = name;
					links_ = new ArrayList();
					ports_ = new ArrayList();
				}
				public void addLink(Node remote_endpoint,
									int local_port,
									int remote_port)
				{
					try
					{
						Link tmp_link = new Link(this, local_port, remote_endpoint, remote_port);
						if (links_.Cast<Link>().Any(link => (Equals(tmp_link.localPort, link.localPort)
						                               &&
						                               Equals(tmp_link.localEndpoint, link.localEndpoint))
						                              ||
						                              (Equals(tmp_link.remotePort, link.remotePort)
						                               &&
						                               Equals(tmp_link.remoteEndpoint, link.remoteEndpoint))))
						{
							throw new MultipleUsingEndpoint(this, local_port);
						}
						links_.Add(tmp_link);
						remote_endpoint.links_.Add(new Link(remote_endpoint, remote_port, this, local_port));
					}
					catch (MultipleUsingEndpoint e)
					{
						Console.WriteLine(e.ToString());
					}
					catch (NodeNotFound e)
					{
						Console.WriteLine(e.ToString());
						throw;
					}
				}
				public string name { get; private set; }
				readonly ArrayList ports_;
				ArrayList links_;

				public ArrayList links
				{
					get
					{
						return links_;
					}
					set
					{
						links_ = value;
					}
				}
			}
		}
	} // namespace Graph
} //namespace ATMCableCloud
